package terminal

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"project-info/src/constants"
	"project-info/src/model"
	routeReq "project-info/src/router/req"
	"time"
	"github.com/gorilla/websocket"
	"golang.org/x/crypto/ssh"

	"project-info/src/lib")

// WebSocket升级器
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 允许跨域，生产环境需要更严格的检查
	},
}

// SSH连接配置
type SSHConfig struct {
	ServerId     int64  `json:"serverId"`
	CredentialId int64  `json:"credentialId"`
	Host         string `json:"host"`
	Port         string `json:"port"`
	Username     string `json:"username"`
	Password     string `json:"password"`
}

// 终端大小
type TerminalSize struct {
	Cols int `json:"cols"`
	Rows int `json:"rows"`
}

// WebSocket消息类型
type WSMessage struct {
	Type         string        `json:"type"`
	Config       *SSHConfig    `json:"config,omitempty"`
	TerminalSize *TerminalSize `json:"terminalSize,omitempty"`
	Data         string        `json:"data,omitempty"`
	Message      string        `json:"message,omitempty"`
}

// SSH连接会话
type SSHSession struct {
	client  *ssh.Client
	session *ssh.Session
	stdin   io.WriteCloser
	ws      *websocket.Conn
}

// SSH WebSocket处理器
func SSHWebSocketHandler(c *lib.GinContext) error {
	// 升级HTTP连接为WebSocket
	ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		log.Printf("WebSocket升级失败: %v", err)
		return err
	}
	defer ws.Close()

	log.Println("新的WebSocket连接建立")
	var sshSession *SSHSession

	// 处理WebSocket消息
	for {
		_, messageBytes, err := ws.ReadMessage()
		if err != nil {
			log.Printf("读取WebSocket消息失败: %v", err)
			break
		}

		var msg WSMessage
		if err := json.Unmarshal(messageBytes, &msg); err != nil {
			log.Printf("解析消息失败: %v", err)
			sendError(ws, "消息格式错误")
			continue
		}

		switch msg.Type {
		case "connect":
			if sshSession != nil {
				closeSSHSession(sshSession)
			}
			sshSession = handleSSHConnect(c, ws, &msg)

		case "input":
			if sshSession != nil && sshSession.stdin != nil {
				if _, err := sshSession.stdin.Write([]byte(msg.Data)); err != nil {
					log.Printf("写入stdin失败: %v", err)
				}
			}

		case "resize":
			if sshSession != nil && sshSession.session != nil {
				if err := sshSession.session.WindowChange(msg.TerminalSize.Rows, msg.TerminalSize.Cols); err != nil {
					log.Printf("调整终端大小失败: %v", err)
				}
			}

		case "disconnect":
			if sshSession != nil {
				closeSSHSession(sshSession)
				sshSession = nil
			}
		}
	}

	// 清理SSH连接
	if sshSession != nil {
		closeSSHSession(sshSession)
	}

	return nil
}

// 处理SSH连接
func handleSSHConnect(c *lib.GinContext, ws *websocket.Conn, msg *WSMessage) *SSHSession {
	if msg.Config == nil || msg.TerminalSize == nil {
		sendError(ws, "缺少连接配置或终端大小信息")
		ws.Close()
		return nil
	}

	config := msg.Config
	termSize := msg.TerminalSize

	err := configUserPassword(c, config)
	if err != nil {
		sendError(ws, fmt.Sprintf("连接配置校验失败: %v", err))
		ws.Close()
		return nil
	}

	// 创建SSH客户端配置
	sshConfig := &ssh.ClientConfig{
		User: config.Username,
		Auth: []ssh.AuthMethod{
			ssh.Password(config.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 生产环境需要更安全的验证
		Timeout:         20 * time.Second,
	}

	// 连接SSH服务器
	addr := fmt.Sprintf("%s:%s", config.Host, config.Port)
	client, err := ssh.Dial("tcp", addr, sshConfig)
	if err != nil {
		log.Printf("SSH连接失败: %v", err)
		sendError(ws, fmt.Sprintf("SSH连接失败: %v", err))
		ws.Close()
		return nil
	}

	// 创建SSH会话
	session, err := client.NewSession()
	if err != nil {
		log.Printf("创建SSH会话失败: %v", err)
		client.Close()
		sendError(ws, fmt.Sprintf("创建SSH会话失败: %v", err))
		ws.Close()
		return nil
	}

	// 设置终端模式
	modes := ssh.TerminalModes{
		ssh.ECHO:          1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}

	// 请求伪终端
	if err := session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, modes); err != nil {
		log.Printf("请求伪终端失败: %v", err)
		session.Close()
		client.Close()
		sendError(ws, fmt.Sprintf("请求伪终端失败: %v", err))
		ws.Close()
		return nil
	}

	// 获取输入输出管道
	stdout, err := session.StdoutPipe()
	if err != nil {
		log.Printf("获取stdout管道失败: %v", err)
		session.Close()
		client.Close()
		sendError(ws, fmt.Sprintf("获取stdout管道失败: %v", err))
		ws.Close()
		return nil
	}

	stderr, err := session.StderrPipe()
	if err != nil {
		log.Printf("获取stderr管道失败: %v", err)
		session.Close()
		client.Close()
		sendError(ws, fmt.Sprintf("获取stderr管道失败: %v", err))
		ws.Close()
		return nil
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		log.Printf("获取stdin管道失败: %v", err)
		session.Close()
		client.Close()
		sendError(ws, fmt.Sprintf("获取stdin管道失败: %v", err))
		ws.Close()
		return nil
	}

	// 启动shell
	if err := session.Shell(); err != nil {
		log.Printf("启动shell失败: %v", err)
		session.Close()
		client.Close()
		sendError(ws, fmt.Sprintf("启动shell失败: %v", err))
		ws.Close()
		return nil
	}

	// 发送连接成功消息
	sendMessage(ws, WSMessage{Type: "connected"})

	sshSession := &SSHSession{
		client:  client,
		session: session,
		stdin:   stdin,
		ws:      ws,
	}

	// 启动输出读取协程
	go func() {
		buffer := make([]byte, 1024)
		for {
			n, err := stdout.Read(buffer)
			if err != nil {
				log.Printf("读取stdout失败: %v", err)
				break
			}
			if n > 0 {
				sendMessage(ws, WSMessage{
					Type: "data",
					Data: string(buffer[:n]),
				})
			}
		}
	}()

	// 启动错误输出读取协程
	go func() {
		buffer := make([]byte, 1024)
		for {
			n, err := stderr.Read(buffer)
			if err != nil {
				log.Printf("读取stderr失败: %v", err)
				break
			}
			if n > 0 {
				sendMessage(ws, WSMessage{
					Type: "data",
					Data: string(buffer[:n]),
				})
			}
		}
	}()

	// 监听会话结束
	go func() {
		session.Wait()
		log.Println("SSH会话已结束")
		sendMessage(ws, WSMessage{Type: "disconnected"})
	}()

	return sshSession
}

func configUserPassword(c *lib.GinContext, config *SSHConfig) error {
	credentialId := config.CredentialId
	serverId := config.ServerId
	// 拼接用户的资源权限子查询
	db, err := routeReq.HandleResourceDataPermission(c, []int8{constants.Server, constants.Credential}, model.DB())
	if err != nil {
		return err
	}
	// 从数据库查询服务器信息
	var resources []model.Resource
	db.Where("id in (?)", []int64{serverId, credentialId}).Find(&resources)

	if len(resources) != 2 {
		return errors.New("权限不足")
	}

	for _, resource := range resources {
		var meta map[string]interface{}
		if err := json.Unmarshal(resource.Meta, &meta); err != nil {
			return err
		}
		if resource.Type == constants.Server {
			config.Host = meta["ip"].(string)
			config.Port = meta["port"].(string)
		}
		if resource.Type == constants.Credential {
			config.Username = meta["userName"].(string)
			config.Password = meta["password"].(string)
		}
	}
	return nil
}

// 关闭SSH会话
func closeSSHSession(session *SSHSession) {
	if session.stdin != nil {
		session.stdin.Close()
	}
	if session.session != nil {
		session.session.Close()
	}
	if session.client != nil {
		session.client.Close()
	}
}

// 发送错误消息
func sendError(ws *websocket.Conn, message string) {
	sendMessage(ws, WSMessage{
		Type:    "error",
		Message: message,
	})
}

// 发送WebSocket消息
func sendMessage(ws *websocket.Conn, msg WSMessage) {
	data, err := json.Marshal(msg)
	if err != nil {
		log.Printf("序列化消息失败: %v", err)
		return
	}

	if err := ws.WriteMessage(websocket.TextMessage, data); err != nil {
		log.Printf("发送WebSocket消息失败: %v", err)
	}
}
